/*
 * Niiuzu
 * Copyright (C) 2011  Thierry Nowak
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.niiuzu.server.dao.impl;

import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.appengine.api.datastore.Transaction;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;
import com.niiuzu.server.dao.exception.CannotCommitTransactionException;
import com.niiuzu.server.dao.exception.DaoException;
import com.niiuzu.server.dao.impl.util.KeyUtil;
import com.niiuzu.server.i18n.ServerI18nMessages;
import com.niiuzu.server.i18n.ServerI18nMessages.Bundle;
import com.niiuzu.shared.model.ClientContext;

/**
 * Tronc commun des implémentations des DAO qui interrogent le magasin de
 * données.
 */
public abstract class AbstractDatastoreDao {

	private static final Logger LOGGER = Logger
			.getLogger(AbstractDatastoreDao.class.getName());

	/**
	 * Crée une nouvelle instance {@link Objectify} pour insérer, supprimer ou
	 * récupérer des données depuis le magasin de données.
	 * 
	 * @param transactional
	 *            <tt>true</tt> si une les entrées/sorties dans le magasin de
	 *            données doivent être gérées par transaction (commit/rollback),
	 *            <tt>false</tt> sinon.
	 */
	protected Objectify createObjectify(boolean transactional) {
		if (mockedObjectify != null) {
			return mockedObjectify;
		} else if (transactional) {
			return ObjectifyService.beginTransaction();
		}
		return ObjectifyService.begin();
	}

	/**
	 * Récupère une instance de {@link KeyUtil}.
	 */
	protected KeyUtil getKeyUtil() {
		if (mockedKeyUtil != null) {
			return mockedKeyUtil;
		}
		return new KeyUtil();
	}

	/**
	 * Commit la transaction.
	 * 
	 * @param objectify
	 *            contenant la transaction à committer.
	 * 
	 * @throws CannotCommitTransactionException
	 *             la transaction ne peut être committée.
	 */
	protected void commit(Objectify objectify)
			throws CannotCommitTransactionException {

		try {
			Transaction transaction = objectify.getTxn();
			transaction.commit();
		} catch (Exception e) {
			throw new CannotCommitTransactionException(e);
		}
	}

	/**
	 * Commit de plusieurs transactions.
	 * 
	 * @param objectifies
	 *            contenant les transactions à committer.
	 * 
	 * @throws CannotCommitTransactionException
	 *             la transaction ne peut être committée.
	 */
	protected void commit(Objectify... objectifies)
			throws CannotCommitTransactionException {

		for (Objectify objectify : objectifies) {
			commit(objectify);
		}
	}

	/**
	 * Rollback de la transaction si celle-ci est active.
	 * 
	 * @param objectify
	 *            contenant la transaction.
	 */
	protected void rollback(Objectify objectify) {
		try {
			if (objectify != null) {
				Transaction transaction = objectify.getTxn();
				if (transaction.isActive()) {
					transaction.rollback();
				}
			}
		} catch (Exception e) {
			LOGGER.log(Level.WARNING,
					"Erreur lors du rollback de la transaction", e);
		}
	}

	/**
	 * Rollback des transaction si celles-ci sont actives.
	 * 
	 * @param objectifies
	 *            contenant les transactions.
	 * 
	 * @throws DaoException
	 *             erreur technique.
	 */
	protected void rollback(Objectify... objectifies) {
		for (Objectify objectify : objectifies) {
			rollback(objectify);
		}
	}

	/**
	 * Récupère le bundle contenant les erreurs.
	 */
	protected ServerI18nMessages getErrorMessages(ClientContext context) {
		return new ServerI18nMessages(Bundle.ErrorMessages, context);
	}

	/* ********************************************************************** */
	/* ********************************************************************** */
	/* ********************************************************************** */

	private Objectify mockedObjectify;
	private KeyUtil mockedKeyUtil;

	/**
	 * JUnit - Définit un mock pour {@link Objectify}.
	 */
	void setMockedObjectify(Objectify mockedObjectify) {
		this.mockedObjectify = mockedObjectify;
	}

	/**
	 * JUnit - Définit un mock pour {@link KeyUtil}.
	 */
	void setMockedKeyUtil(KeyUtil mockedKeyUtil) {
		this.mockedKeyUtil = mockedKeyUtil;
	}
}
